#include <TrueText/Atlas.h>

#include <TrueText/Font.h>
#include "FontImpl.h"
#include <boost/smart_ptr/shared_ptr.hpp>
#include "AtlasLayouter.h"
#include "ftutils.h"
#include "DrawersBuilder.h"
#include "Drawer.h"
#include "BitmapLayer.h"

namespace TrueText {

struct AtlasImpl
{
	AtlasImpl(boost::shared_ptr<FontImpl> font)
	{
		DrawersBuilder builder(font);
		drawers = builder.Build();
		clipWidth = -1;
	}

	void Invalidate() { layout.reset(); }
	bool NeedsRebuild() const { return !layout.get(); }

	void Rebuild();

	int clipWidth;
	std::wstring string;
	boost::shared_ptr<FontImpl> font;
	boost::shared_ptr<AtlasLayout> layout;
	std::vector<boost::shared_ptr<Drawer> > drawers;
	TextBounds bounds;
};

void AtlasImpl::Rebuild()
{
	AtlasLayouter layouter(font);
	layout = layouter.Layout(clipWidth, string.c_str());

	Rectangle partialBounds(
		std::numeric_limits<int>::max(), std::numeric_limits<int>::max(), 
		std::numeric_limits<int>::min(), std::numeric_limits<int>::min());

	for (std::map<wchar_t, LetterMetrics>::const_iterator i = layout->Metrics->Letters.begin(); i != layout->Metrics->Letters.end(); ++i)
	{
		const Rectangle& bounds = i->second.bounds;

		partialBounds.left = std::min(partialBounds.left, bounds.left);
		partialBounds.top = std::min(partialBounds.top, bounds.top);
		partialBounds.right = std::max(partialBounds.right, bounds.right);
		partialBounds.bottom = std::max(partialBounds.bottom, bounds.bottom);
	}

	layout->Metrics->Offset = Point(-partialBounds.left, -partialBounds.top);

	bounds.SetDimensions(Dimensions(partialBounds.right - partialBounds.left + 1, partialBounds.bottom - partialBounds.top + 1));
	TT_ASSERT(bounds.GetDimensions().Width <= clipWidth);
	int lastBaseline = (int)(font->GetLineHeight() * layout->Layout->Metrics.size());

	bounds.SetBottomBaselineDistance(bounds.GetDimensions().Height - lastBaseline);
}

Atlas::Atlas(const Font& font)
	: impl(new AtlasImpl(font.getImpl()))
{
	impl->font = font.getImpl();
	impl->clipWidth = -1;
}

Atlas::~Atlas()
{
}

const std::wstring& Atlas::GetString() const
{
	return impl->string;
}

void Atlas::SetString(const std::wstring& value)
{
	impl->string = value;
	impl->Invalidate();
}

int Atlas::GetClipWidth() const
{
	return impl->clipWidth;
}

void Atlas::SetClipWidth(int value)
{
	impl->clipWidth = value;
	impl->Invalidate();
}

void Atlas::Render(const BitmapInfo& output)
{
	if (impl->NeedsRebuild())
		impl->Rebuild();

	for (std::vector<boost::shared_ptr<Drawer> >::const_iterator drawer = impl->drawers.begin(); drawer != impl->drawers.end(); ++drawer)
	{
		boost::shared_ptr<BitmapLayer> layer = (*drawer)->Draw(*impl->layout->Layout.get());
		layer->SetOffset(impl->layout->Metrics->Offset);
		layer->Render(output);
	}
}

const TextBounds& Atlas::GetBounds()
{
	if (impl->NeedsRebuild())
		impl->Rebuild();

	return impl->bounds;
}

boost::shared_ptr<AtlasMetrics> Atlas::GetMetrics()
{
	if (!impl->layout.get())
		return boost::shared_ptr<AtlasMetrics>();

	return impl->layout->Metrics;
}

} // namespace TrueText
